# (2)queue同步队列
'''
该queue模块实现多生产者，多用户队列。当在多线程之间必须安全地交换信息时，它在线程编程中特别有用。
该Queue模块中的类实现了所有必需的锁定语义
该模块实现三种类型的队列，它们的区别仅在于检索条目的顺序：
在FIFO队列中，第一个添加的任务是第一个被检索的。
在LIFO队列中，最后添加的条目是第一个被检索（像堆栈一样操作）。
使用优先级队列，条目保持排序（使用heapq模块），并且首先检索最低值的条目。
在内部，模块使用锁来暂时阻止竞争的线程; 
但是，它不是为了处理线程内的重入而设计的；该queue模块定义了以下类：
class queue.Queue（maxsize = 0 ）
FIFO队列的构造器。 maxsize是一个整数，用于设置可放入队列的项目数的上限。
一旦达到此大小，插入将会阻塞，直到队列项被消耗。如果 maxsize小于或等于零，队列大小是无限的。
class queue.LifoQueue（maxsize = 0 ）
LIFO队列的构造器。 maxsize是一个整数，用于设置可放入队列的项目数的上限。
一旦达到此大小，插入将会阻塞，直到队列项被消耗。如果 maxsize小于或等于零，队列大小是无限的。
class queue.PriorityQueue（maxsize = 0 ）
优先队列的构造函数。 maxsize是一个整数，用于设置可放入队列的项目数的上限。
一旦达到此大小，插入将会阻塞，直到队列项被消耗。如果 maxsize小于或等于零，队列大小是无限的。
首先检索最低值的条目（最低值条目是返回的条目sorted(list(entries))[0]）。
对于条目的典型图案的形式是一个元组：。(priority_number, data)
exception queue.Empty
在空对象上调用非阻塞get()（或 get_nowait()）时引发异常Queue。
exception queue.Full
如果在已满的对象上调用非阻塞put()（或 put_nowait()），则会引发异常Queue。
队列对象（Queue,LifoQueue或PriorityQueue）提供以下的公共方法：
Queue.qsize（）
返回队列的大小。请注意，qsize（）> 0并不保证后续的get（）不会被阻塞，
qsize（）也不会保证put（）不会被阻塞。
Queue.empty（）
如果队列为空则返回True，否则返回False。如果empty（）返回True，则不保证对put（）的后续调用不会被阻塞。
同样，如果empty（）返回False，则不保证后续调用get（）不会被阻塞。
Queue.full（）
如果队列已满则返回True，否则返回Fales。如果full（）返回True，则不保证后续的get（）调用不会被阻塞。
同样，如果full（）返回False它并不能保证后续调用put（）不会被阻塞。
Queue.put（item，block = True，timeout = None ）
将项目放入队列中。如果可选参数block为True，并且timeout为 None（默认），则根据需要进行阻止，直到空闲插槽可用。
如果 timeout是一个正数，则最多会阻塞timeout秒数，Full如果在此时间内没有空闲插槽，则会引发异常。否则（block为false），
如果一个空闲插槽立即可用，则在队列中放置一个项目，否则引发Full异常（在这种情况下timeout被忽略）。
Queue.put_nowait（item ）
相当于。put(item, False)
Queue.get（block = True，timeout = None ）
从队列中移除并返回一个项目。如果可选参数块为真，并且 timeout为None（默认），则在必要时阻塞，直到项目可用。
如果timeout是一个正数，则最多会阻塞timeout秒数，Empty如果在该时间内没有可用项目，则会引发异常。
否则（block为false），返回一个项目，如果一个是立即可用的，否则引发Empty异常（在这种情况下timeout被忽略）。
Queue.get_nowait（）
相当于get(False)。
提供了两种方法来支持跟踪入队任务是否已完全由守护进程消费者线程处理。
Queue.task_done（）
表明以前排队的任务已经完成。由队列消费者线程使用。
对于每个get()用于获取任务的对象，随后的调用都会task_done()告诉队列，任务的处理已完成。
如果a join()当前被阻塞，则在处理所有项目时（意味着task_done()已经接收到已经put()进入队列的每个项目的呼叫），则将恢复。
提出了一个ValueError好象叫更多的时间比中放入队列中的项目。
Queue.join（）
阻塞，直到队列中的所有项目都被获取并处理。
每当将项目添加到队列中时，未完成任务的数量就会增加。
只要消费者线程调用task_done()来指示该项目已被检索，并且所有工作都已完成，计数就会减少。
当未完成任务的数量下降到零时，join()取消阻止
'''
# 1.Queue先进先出队列
import queue
def show(q, i):
    if q.empty() or q.qsize() >= 1:
        q.put(i)  # 存队列
    elif q.full():
        print('queue not size')
que = queue.Queue(5)  # 允许5个队列的队列对象
for i in range(5):
    show(que, i)
print('queue is number:', que.qsize())  # 队列元素个数
for j in range(5):
    print(que.get())  # 取队列
print('......end')


# ２.LifoQueue先进后出队列
lifoque = queue.LifoQueue()
lifoque.put('hello1')
lifoque.put('hello2')
lifoque.put('hello3')
print(lifoque.qsize())# 队列元素个数
print(lifoque.get())
print(lifoque.get())
print(lifoque.get())

import queue
#PriorityQueue按数据大小取最小值优先
pque =queue.PriorityQueue()
pque.put(7)
pque.put(5)
pque.put(3)
pque.put(8)
pque.put(100)
print(pque.get())


# 信号量(Semaphore)
import threading,time
class mythreading(threading.Thread): #写一个类方法继承hreading模块
    def run(self):  #运行线程的函数，函数名必须是run名称
        semaphore.acquire()  #获取信号量锁
        print('running the thread:',self.getName())
        time.sleep(2)
        semaphore.release()  #释放信号量锁

if __name__ == '__main__':
    semaphore = threading.BoundedSemaphore(3) #创建信号量对象，只运行3个进程同时运行
    for i in range(20):
        t1 = mythreading()
        t1.start()
    t1.join()
    print('---end')

# 事件(event)
'''
python线程的事件用于主线程控制其它线程的执行，
事件主要提供了三个方法：
event.wait  线程阻塞
event.set   将全局对象“Flag”设置为False
event.clear 将全局对象"Flag"设置为True
事件处理的机制：全局定义了一个“Flag”，默认为“False”，如果“Flag”值为 False，
那么当程序执行 event.wait 方法时就会阻塞，如果“Flag”值为True，那么event.
wait 方法时便不再阻塞。
'''
import threading
# def show(event):
#     print('start')
#     event.wait()  #阻塞线程执行程序
#     print('done')
# event_obj = threading.Event()  #创建event事件对象
# for i in range(10):
#     t1 = threading.Thread(target=show,args=(event_obj,))
#     t1.start()
#     inside = input('>>>:')
#     if inside == '1':
#         event_obj.set() #当用户输入1时，set全局Flag为True,线程不再阻塞，打印"done"
#     event_obj.clear() #将Flag设置为False

# 条件(Condition)
'''使得线程等待，只有满足某条件时，才释放N个线程'''
import threading
def condition_func():
    ret = False
    inp = input('>>>:')
    if inp == '1':
        ret = True
    return ret
def run(n):
    con.acquire()  # 条件锁
    con.wait_for(condition_func)  # 判断条件
    print('running...', n)
    con.release()  # 释放锁
    print('------------')
if __name__ == '__main__':
    con = threading.Condition()  # 建立线程条件对象
    for i in range(10):
        t = threading.Thread(target=run, args=(i,))
        t.start()
        t.join()
    print('退出程序')